/**
 * 
 */
package ro.dta.idbi.ui;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.Toolkit;
import java.io.File;
import java.io.FileOutputStream;
import java.util.concurrent.ExecutionException;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingWorker;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.jibble.epsgraphics.EpsGraphics2D;

import ro.dta.idbi.interfaces.ApplicationSections;
import ro.dta.idbi.interfaces.IProject;
import ro.dta.idbi.interfaces.IntegrationOperation;
import ro.dta.idbi.interfaces.ui.IGuiFactory;
import ro.dta.idbi.interfaces.ui.IHistoryPanel;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.interfaces.ui.IProjectPane;
import ro.dta.idbi.interfaces.ui.IRepositoryBrowser;
import ro.dta.idbi.model.IntegrationProject;
import ro.dta.idbi.ui.sql.SqlSchemaPanel;
import uk.ac.ic.doc.automed.NotFoundException;
import uk.ac.ic.doc.automed.reps.Schema;

/**
 * Main frame of the application
 * 
 * @author Tudor Dobrila
 */
public class MainFrame extends JFrame implements IMainFrame, ChangeListener {

	public static int projectCount = 1;

	private static final long serialVersionUID = -2450981545021172261L;

	/**
	 * Application status bar
	 */
	private final transient JLabel statusLabel;

	/**
	 * Right hand side tabbed pane
	 */
	private final transient JTabbedPane rightPane;

	/**
	 * Repository browser
	 */
	private final transient RepositoryBrowserPanel repBrowser;

	/**
	 * History panel
	 */
	private transient HistoryPanel historyPane;

	/**
	 * GUI Factory used to create various UI elements and break dependencies
	 */
	private final transient IGuiFactory guiFactory;

	/**
	 * Default title of the application
	 */
	private final transient String defaultTitle;

	/**
	 * True if repository browser should be shown, false otherwise
	 */
	private transient boolean showBrowser;

	/**
	 * Splitter used to show repository browser on left hand side and the tabbed
	 * pane on the right hand side
	 */
	private final transient JSplitPane splitPane;

	/**
	 * Left split pane used to separate the repository browser and the command
	 * history
	 */
	private transient JSplitPane leftSp;

	/**
	 * Last known location of the splitter
	 */
	private transient int lastSplitLocation;

	/**
	 * Menu bar of the main application
	 */
	private final transient MainMenuBar menuBar;

	/**
	 * MainFrame class constructor
	 * 
	 * @param guiFactory
	 *            GUI Factory used to create dialogs and UI elements depending
	 *            on the factory
	 * @param defaultTitle
	 *            Default title of the application
	 */
	public MainFrame(final IGuiFactory guiFactory, final String defaultTitle) {
		super();
		this.guiFactory = guiFactory;
		this.defaultTitle = defaultTitle;

		super.setTitle(defaultTitle);
		menuBar = new MainMenuBar(this);
		setJMenuBar(menuBar);
		setIconImage(Toolkit.getDefaultToolkit().getImage("./resources/icon.png"));
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		statusLabel = new JLabel();
		statusLabel.setForeground(new Color(55, 55, 55));
		statusLabel.setBorder(BorderFactory.createLineBorder(new Color(200, 200, 200)));
		getContentPane().add(statusLabel, BorderLayout.SOUTH);

		rightPane = new JTabbedPane();
		rightPane.addChangeListener(this);

		final RightPanel mainPanel = new RightPanel("Quick Options", new FirstScreenPanel(this));
		rightPane.addTab("Quick Options", new ImageIcon("./resources/global.png"), mainPanel);
		rightPane.setTabComponentAt(0, new CloseableTab(rightPane));

		repBrowser = new RepositoryBrowserPanel(this);
		showBrowser = true;
		lastSplitLocation = 200;

		splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, repBrowser, rightPane);
		splitPane.setDividerSize(5);
		splitPane.setDividerLocation(lastSplitLocation);

		getContentPane().add(splitPane, BorderLayout.CENTER);

		if (!checkConfigurationFile()) {
			JOptionPane.showMessageDialog(this,
					"The AutoMed configuration file does not exist. Please reconfigure AutoMed!",
					"Error", JOptionPane.ERROR_MESSAGE);
			System.exit(1);
		}
	}

	/**
	 * Check whether the configuration file for AutoMed exists
	 * 
	 * @return True if configuration file exists, false otherwise
	 */
	protected static boolean checkConfigurationFile() {
		final String cfgFile = System.getProperty("user.home")
				+ System.getProperty("file.separator") + ".automed"
				+ System.getProperty("file.separator") + "data_source_repository.cfg";

		return new File(cfgFile).exists();
	}

	@Override
	public void setStatusBarText(final String newText) {
		statusLabel.setText(" " + newText);
	}

	@Override
	public JTabbedPane getRightPane() {
		return rightPane;
	}

	@Override
	public void open(final ApplicationSections section, final Object[] params,
			final String tabTitle, final Icon tabIcon, final String title,
			final String successMessage) {
		final int tabIndex = indexOfTab(tabTitle);

		// Check if tab is not already open
		if (tabIndex != -1) {
			rightPane.setSelectedIndex(tabIndex);
			return;
		}

		// Create tab and load content
		final RightPanel newPanel = new RightPanel(title, null);
		newPanel.showLoading();
		rightPane.addTab(tabTitle, tabIcon, newPanel);
		rightPane.setSelectedIndex(rightPane.getTabCount() - 1);
		rightPane.setTabComponentAt(rightPane.getTabCount() - 1, new CloseableTab(rightPane));
		statusLabel.setText("Loading...");

		final SwingWorker<JPanel, Void> worker = new SwingWorker<JPanel, Void>() {

			@Override
			protected JPanel doInBackground() {
				switch (section) {
				case VIEW_SQL_SCHEMA:
					if (params.length != 1) {
						throw new IllegalArgumentException("The name of the schema is required.");
					}
					String schemaName = params[0].toString();
					try {
						final Schema schema = Schema.getSchema(schemaName);
						SqlSchemaPanel sqlPanel = new SqlSchemaPanel(MainFrame.this, schema);
						return sqlPanel;
					} catch (NotFoundException e) {
						JOptionPane.showMessageDialog(MainFrame.this, "Schema " + schemaName
								+ " does not exist in the AutoMed repository.", "Error",
								JOptionPane.ERROR_MESSAGE);
					}
					break;
				case PROJECT:
					IProject project = null;
					if (params.length == 0) {
						// New project
						project = new IntegrationProject();
					} else if (params.length == 1 && params[0] instanceof IProject) {
						project = (IProject) params[0];
					} else {
						throw new IllegalArgumentException(
								"Expected zero or one parameters, got too many.");
					}
					ProjectPanel projectPanel = new ProjectPanel(MainFrame.this, newPanel, project);
					menuBar.setSaveEnabled(true);
					menuBar.setSaveAsEnabled(true);
					return projectPanel;
				case SCHEMAS_INTEGRATION:
					if (params.length != 2) {
						throw new IllegalArgumentException(
								"Expected parameters: schemas array and integration operation.");
					}
					IntegrationPanel intPanel;
					try {
						intPanel = new IntegrationPanel(MainFrame.this, (String[]) params[0],
								(String[]) params[0], (IntegrationOperation) params[1]);
						return intPanel;
					} catch (NotFoundException e) {
						JOptionPane.showMessageDialog(MainFrame.this,
								"Could not open integration panel!", "Error",
								JOptionPane.ERROR_MESSAGE);
					}
				default:
				}

				return null;
			}

			@Override
			protected void done() {
				MainFrame.this.statusLabel.setText(successMessage);
				try {
					JPanel result = get();
					newPanel.setContent(result, GridBagConstraints.BOTH);
					updateLeftPane();
				} catch (InterruptedException e) {
					JOptionPane.showMessageDialog(MainFrame.this,
							"Could not change active section Execution has been interrupted.\r\nReason: "
									+ e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
				} catch (ExecutionException e) {
					JOptionPane.showMessageDialog(MainFrame.this,
							"Could not change active section.\r\nReason: " + e.getMessage(),
							"Error", JOptionPane.ERROR_MESSAGE);
				}
			}

		};

		worker.execute();
	}

	/**
	 * Find the index of a tab, given its name
	 * 
	 * @return Index of tab, if found, -1 otherwise
	 */
	private int indexOfTab(final String name) {
		final int count = rightPane.getTabCount();
		int result = -1;
		for (int i = 0; i < count; i++) {
			if (rightPane.getTitleAt(i).equals(name)) {
				result = i;
				break;
			}
		}

		return result;
	}

	@Override
	public IRepositoryBrowser getRepositoryBrowser() {
		return repBrowser;
	}

	@Override
	public IGuiFactory getGuiFactory() {
		return guiFactory;
	}

	@Override
	public void setTitle(final String title) {
		if ("".equals(title)) {
			super.setTitle(defaultTitle);
		} else {
			super.setTitle(defaultTitle + " - " + title);
		}
	}

	@Override
	public void showRepBrowser(final boolean selected) {
		if (selected == showBrowser) {
			return;
		}
		showBrowser = selected;

		if (selected) {
			splitPane.setDividerLocation(lastSplitLocation);
			splitPane.setDividerSize(5);

		} else {
			lastSplitLocation = splitPane.getDividerLocation();
			splitPane.setDividerLocation(0);
			splitPane.setDividerSize(0);
		}
	}

	/**
	 * Update the left pane, containing the command history panel and the
	 * repository browser
	 */
	private void updateLeftPane() {
		if (rightPane.getSelectedComponent() instanceof RightPanel) {
			final RightPanel tabContent = (RightPanel) rightPane.getSelectedComponent();

			if (tabContent.getContent() instanceof IProjectPane) {
				if (historyPane == null) {
					historyPane = new HistoryPanel(this);
				}

				leftSp = new JSplitPane(JSplitPane.VERTICAL_SPLIT, repBrowser, historyPane);
				leftSp.setDividerSize(5);
				leftSp.setDividerLocation((int) (getHeight() * 0.6));

				splitPane.setLeftComponent(leftSp);
			} else {
				if (repBrowser != null) {
					splitPane.setLeftComponent(repBrowser);
				}
			}
		}
		if (splitPane != null) {
			splitPane.setDividerLocation(lastSplitLocation);
		}
	}

	@Override
	public void stateChanged(final ChangeEvent event) {
		updateLeftPane();

		final JTabbedPane source = (JTabbedPane) event.getSource();
		if (source.getSelectedComponent() instanceof RightPanel) {
			final RightPanel tabContent = (RightPanel) source.getSelectedComponent();

			if (tabContent.getContent() instanceof IProjectPane) {
				menuBar.setSaveEnabled(true);
				menuBar.setSaveAsEnabled(true);
				return;
			}
		}

		menuBar.setSaveEnabled(false);
		menuBar.setSaveAsEnabled(false);
	}

	@Override
	public IHistoryPanel getHistoryPanel() {
		return historyPane;
	}

	/**
	 * Get a screenshot of the frame window
	 * 
	 * @return Screenshot of the main frame
	 */
	public boolean saveScreenshot(String path) {
		Dimension dim = this.getPreferredSize();
		try {
			EpsGraphics2D graphics = new EpsGraphics2D("", new FileOutputStream(path), 0, 0,
					dim.width, dim.height);
			this.paint(graphics);
			graphics.setColor(Color.WHITE);
			graphics.setStroke(new BasicStroke(1));
			graphics.close();
			return true;
		} catch (Exception e) {
			JOptionPane.showMessageDialog(this,
					"Could not save screenshot of the main frame. Reason:\r\n" + e.getMessage());
		}

		return false;
	}
}
